/*
 *  levelUpGame - AudioController.cpp
 *  Copyright 2011 W. Reckman. All rights reserved.
 */
#include <string>
#include "ofSoundPlayer.h"
#include "ofUtils.h"
#include "Settings.h"
#include "LevelUpApp.h"
#include "AudioController.h"

using std::string;


//NOTE: instead of x-fading music at balance=0.5, change it at 0.5 -/+ BALANCE_CHANGE_CENTER_DIST
const float AudioController::BALANCE_CHANGE_CENTER_DIST = 0.1f;

AudioController::AudioController( const LevelUpApp& appRef_ )
: appRef( appRef_ ), s( Settings::getInstance() ), xFadeStartTime( -1 ),
  musicMuted( false ), lastMusicVolume( 1.0f )
{}

AudioController::~AudioController() {
	map_SOUNDS_ofSoundPlayerP::iterator itgs;
	for ( itgs = globalSounds.begin(); itgs != globalSounds.end(); ++itgs ) {
		delete (*itgs).second;
	}
	globalSounds.clear();
	
	vec_PlayerSounds::iterator itp;
	for ( itp = playerSounds.begin(); itp != playerSounds.end(); ++itp ) {
		PlayerSounds& ps = *itp;
		delete ps.sndId;
		delete ps.sndTurn;
	}
	playerSounds.clear();
}


void AudioController::setup() {
	static const int numPlayerSounds = 3;
	
	ofLog( OF_LOG_NOTICE, "(AudioController.cpp) loading audio" );
	
	ofSoundPlayer* sp;
	
	string cntStartName = *s.getValueInGroup( "snd_countdown_start", "resource", "" );
	string cntEndName = *s.getValueInGroup( "snd_countdown_end", "resource", "" );
	string voiceGetreadyName = *s.getValueInGroup( "snd_voice_getready", "resource", "" );
	string voiceGoName = *s.getValueInGroup( "snd_voice_go", "resource", "" );
	string voicePositionsName = *s.getValueInGroup( "snd_voice_positions", "resource", "" );
	string voice3Name = *s.getValueInGroup( "snd_voice_3", "resource", "" );
	string voice2Name = *s.getValueInGroup( "snd_voice_2", "resource", "" );
	string voice1Name = *s.getValueInGroup( "snd_voice_1", "resource", "" );
	string winName = *s.getValueInGroup( "snd_win", "resource", "" );
	string dieName = *s.getValueInGroup( "snd_die", "resource", "" );
	string puCultName = *s.getValueInGroup( "snd_pickup_culture", "resource", "" );
	string puIndName = *s.getValueInGroup( "snd_pickup_industry", "resource", "" );
	string plIdleName = *s.getValueInGroup( "snd_idle_player", "resource", "" );
	string plIdPrefix = *s.getValueInGroup( "snd_player_id_prefix", "resource", "" );
	string plIdSuffix = *s.getValueInGroup( "snd_player_id_suffix", "resource", "" );
	string plTurnPrefix = *s.getValueInGroup( "snd_player_turn_prefix", "resource", "" );
	string plTurnSuffix = *s.getValueInGroup( "snd_player_turn_suffix", "resource", "" );
	string musicIndustryName = *s.getValueInGroup( "snd_music_industry", "resource", "" );
	string musicCultureName = *s.getValueInGroup( "snd_music_culture", "resource", "" );
	
	xFadeDuration = ofToFloat( *s.getValueInGroup( "snd_music_xfade_duration", "resource", "1" ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( cntStartName );
	sp->setMultiPlay( true );
	globalSounds.insert( std::make_pair( SND_COUNTDOWN_START, sp ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( cntEndName );
	sp->setMultiPlay( true );
	globalSounds.insert( std::make_pair( SND_COUNTDOWN_END, sp ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( voiceGetreadyName );
	globalSounds.insert( std::make_pair( SND_VOICE_GETREADY, sp ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( voicePositionsName );
	globalSounds.insert( std::make_pair( SND_VOICE_POSITIONS, sp ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( voiceGoName );
	globalSounds.insert( std::make_pair( SND_VOICE_GO, sp ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( voice3Name );
	globalSounds.insert( std::make_pair( SND_VOICE_3, sp ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( voice2Name );
	globalSounds.insert( std::make_pair( SND_VOICE_2, sp ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( voice1Name );
	globalSounds.insert( std::make_pair( SND_VOICE_1, sp ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( winName );
	globalSounds.insert( std::make_pair( SND_WIN, sp ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( dieName );
	globalSounds.insert( std::make_pair( SND_DIE, sp ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( puCultName );
	sp->setMultiPlay( true );
	globalSounds.insert( std::make_pair( SND_PICKUP_CULTURE, sp ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( puIndName );
	sp->setMultiPlay( true );
	globalSounds.insert( std::make_pair( SND_PICKUP_INDUSTRY, sp ) );
	
	sp = new ofSoundPlayer();
	sp->loadSound( plIdleName );
	sp->setMultiPlay( true );
	globalSounds.insert( std::make_pair( SND_IDLE, sp ) );
	
	for ( int i = 0; i < numPlayerSounds; ++i ) {
		PlayerSounds psTemp;
		
		sp = new ofSoundPlayer();
		sp->loadSound( plIdPrefix + ofToString( i + 1 ) + plIdSuffix );
		psTemp.sndId = sp;
		
		sp = new ofSoundPlayer();
		sp->loadSound( plTurnPrefix + ofToString( i + 1 ) + plTurnSuffix );
		psTemp.sndTurn = sp;
		
		playerSounds.push_back( psTemp );
	}
	
	musicIndustry.loadSound( musicIndustryName );
	musicCulture.loadSound( musicCultureName );
	musicIndustry.setLoop( true );
	musicCulture.setLoop( true );
	float balance = appRef.getGame().getBalanceMeter().getBalanceFloat();
	lastBalance = balance;
	
	if ( balance <= 0.5f ) {
		musicIndustry.setVolume( 1.0f );
		musicCulture.setVolume( 0.0f );
	} else {
		musicIndustry.setVolume( 0.0f );
		musicCulture.setVolume( 1.0f );
	}
	musicIndustry.play();
	musicCulture.play();
	//restartMusic();
}

void AudioController::update() {
	const GameModel& g = appRef.getGame();
	float balance = g.getBalanceMeter().getBalanceFloat();
	
	BALANCE_KIND lb = balanceToKind( lastBalance );
	BALANCE_KIND cb = balanceToKind( balance );

	if ( ( lb != BK_INDUSTRY && cb == BK_INDUSTRY ) || ( lb != BK_CULTURE && cb == BK_CULTURE ) ) {
ofLog( OF_LOG_NOTICE, "(AudioController.cpp) switching music balance to %s", balanceText( cb ).c_str() ); //TEMP
		xFadeStartTime = g.getTime();
		xFadeDirection = cb;
	}
	
	if ( xFadeStartTime >= 0 ) {
		float elapsed = g.getTime() - xFadeStartTime;
		if ( elapsed >= xFadeDuration ) xFadeStartTime = -1;
		
		if ( xFadeDirection == BK_INDUSTRY ) {
			float f = ofMap( elapsed, 0, xFadeDuration, 0.0f, 1.0f, true );
			musicIndustry.setVolume( f );
			musicCulture.setVolume( 1.0f - f );
		} else if ( xFadeDirection == BK_CULTURE ) {
			float f = ofMap( elapsed, 0, xFadeDuration, 0.0f, 1.0f, true );
			musicIndustry.setVolume( 1.0f - f );
			musicCulture.setVolume( f );
		}
	}
	
	lastBalance = balance;
}


void AudioController::play( SOUNDS snd_, int playerId_ ) {
	ofSoundPlayer* sp = fetchSoundPlayer( snd_, playerId_ );
	if ( sp ) sp->play();
}

void AudioController::stop( SOUNDS snd_, int playerId_ ) {
	ofSoundPlayer* sp = fetchSoundPlayer( snd_, playerId_ );
	if ( sp ) sp->stop();
}


void AudioController::restartMusic() {
	musicIndustry.stop(); musicCulture.stop();
	musicIndustry.play(); musicCulture.play();
	musicIndustry.setPosition( 0.0f ); musicCulture.setPosition( 0.0f );
}

void AudioController::setMusicVolume( float vol_ ) {
	lastMusicVolume = vol_;
	musicIndustry.setVolume( vol_ );
	musicCulture.setVolume( vol_ );
}

bool AudioController::isMusicMuted() const { return musicMuted; }

void AudioController::setMusicMuted( bool muted_ ) {
	float newVol;
	if ( muted_ ) { newVol = 0.0f; musicMuted = true; }
	else { newVol = lastMusicVolume; musicMuted = false; }
	musicIndustry.setVolume( newVol );
	musicCulture.setVolume( newVol );
}


void AudioController::setLoop( SOUNDS snd_, bool loop_, int playerId_ ) {
	ofSoundPlayer* sp = fetchSoundPlayer( snd_, playerId_ );
	if ( sp ) sp->setLoop( loop_ );
}

void AudioController::setMultiPlay( SOUNDS snd_, bool mp_, int playerId_ ) {
	ofSoundPlayer* sp = fetchSoundPlayer( snd_, playerId_ );
	if ( sp ) sp->setMultiPlay( mp_ );
}

void AudioController::setSpeed( SOUNDS snd_, float speed_, int playerId_ ) {
	ofSoundPlayer* sp = fetchSoundPlayer( snd_, playerId_ );
	if ( sp ) sp->setSpeed( speed_ );
}

void AudioController::setPan( SOUNDS snd_, float pan_, int playerId_ ) {
	ofSoundPlayer* sp = fetchSoundPlayer( snd_, playerId_ );
	if ( sp ) sp->setPan( pan_ );
}

void AudioController::setVolume( SOUNDS snd_, float vol_, int playerId_ ) {
	ofSoundPlayer* sp = fetchSoundPlayer( snd_, playerId_ );
	if ( sp ) sp->setVolume( vol_ );
}

void AudioController::setPosition( SOUNDS snd_, float pos_, int playerId_ ) {
	ofSoundPlayer* sp = fetchSoundPlayer( snd_, playerId_ );
	if ( sp ) sp->setPosition( pos_ );
}


/*********************
 * PRIVATE FUNCTIONS *
 *********************/

ofSoundPlayer* AudioController::fetchSoundPlayer( SOUNDS snd_, int playerId_ ) {
	switch ( snd_ ) {
		case SND_COUNTDOWN_START: case SND_COUNTDOWN_END:
		case SND_VOICE_GETREADY: case SND_VOICE_GO: case SND_VOICE_POSITIONS:
		case SND_VOICE_3: case SND_VOICE_2: case SND_VOICE_1:
		case SND_PICKUP_CULTURE: case SND_PICKUP_INDUSTRY:
		case SND_WIN: case SND_DIE: case SND_IDLE:
			return globalSounds[snd_];
			break;
		case SND_PLAYER_ID:
			if ( playerId_ >= 0 && playerId_ < playerSounds.size() ) return playerSounds[playerId_].sndId;
			break;
		case SND_PLAYER_TURN:
			if ( playerId_ >= 0 && playerId_ < playerSounds.size() ) return playerSounds[playerId_].sndTurn;
			break;
	}
	
	return 0;
}

BALANCE_KIND AudioController::balanceToKind( float balance_ ) const {
	if ( balance_ <= -BALANCE_CHANGE_CENTER_DIST ) return BK_CULTURE;
	else if ( balance_ >= BALANCE_CHANGE_CENTER_DIST ) return BK_INDUSTRY;
	else return BK_NEUTRAL;
}
